import Dep from './observer/dep';
import { observer } from './observer/index';
import Watcher, { nextTick } from './observer/watcher';

export function initState(vm) {
  let opts = vm.$options;
  if (opts.data) {
    initData(vm);
  }
  if (opts.props) {
    initProps();
  }
  if (opts.methods) {
    initMethods();
  }
  if (opts.watch) {
    initWatch(vm);
  }
  if (opts.computed) {
    initComputed(vm);
  }
}

function initData(vm) {
  let data = vm.$options.data;
  data = typeof data === 'function' ? data.call(vm) : data;
  vm._data = data;

  // 数据劫持
  observer(data);

  // data 中的内容代理到实例上，this.xxx === this._data.xxx
  for (let key in data) {
    proxy(vm, '_data', key);
  }
}

function proxy(vm, source, key) {
  Object.defineProperty(vm, key, {
    get() {
      return vm[source][key];
    },
    set(newValue) {
      vm[source][key] = newValue;
    },
  });
}

function initProps() {}
function initMethods() {}

function initWatch(vm) {
  const watch = vm.$options.watch;
  for (let key in watch) {
    const handler = watch[key];

    if (Array.isArray(handler)) {
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i]);
      }
    } else {
      createWatcher(vm, key, handler);
    }
  }
}

function createWatcher(vm, key, handler) {
  if (typeof handler === 'string') {
    handler = vm[handler];
  }

  return vm.$watch(key, handler);
}

function initComputed(vm) {
  const computed = vm.$options.computed;
  const watchers = (vm._computedWatchers = {});
  for (let key in computed) {
    let userDef = computed[key];
    const fn = typeof userDef === 'function' ? userDef : userDef.get;
    // 创建计算属性 watcher ，用于获取计算属性的 getter
    // lazy 标识不会立即执行 watcher.get 函数，计算属性 watcher 不会入栈
    watchers[key] = new Watcher(vm, fn, { lazy: true });
    defineComputed(vm, key, userDef);
  }
}

function defineComputed(target, key, userDef) {
  // const getter = typeof userDef === 'function' ? userDef : userDef.get;
  const setter = userDef.set || (() => {});
  Object.defineProperty(target, key, {
    get: createComputedGetter(key),
    set: setter,
  });
}

function createComputedGetter(key) {
  return function () {
    const watcher = this._computedWatchers[key];
    // 多次求值 只有第一次生效
    if (watcher.dirty) {
      // 计算 value，调用 watcher get
      watcher.evaluate();
    }
    if (Dep.target) {
      // 栈里还有渲染 watcher
      watcher.depend();
    }
    return watcher.value;
  };
}

export function initStateMixin(Vue) {
  Vue.prototype.$nextTick = nextTick;
  Vue.prototype.$watch = function (exprOrFn, cb) {
    new Watcher(this, exprOrFn, { user: true }, cb);
  };
}
